package com.zhilei.filter;

import com.google.gson.Gson;
import com.zhilei.base.BaseInfoProperties;
import com.zhilei.grace.result.GraceJSONResult;
import com.zhilei.grace.result.ResponseStatusEnum;
import com.zhilei.untils.JWTUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.List;


/**
 * JWT 鉴权过滤器,实现全局过滤器的接口GlobalFilter，配置过滤器的级别Order
 *
 * @author 志磊
 */
@Component
@Slf4j
public class SecurityFilterJWT extends BaseInfoProperties implements GlobalFilter, Ordered {

    // 请求头里面token的key
    private static final String HEADER_USER_TOKEN = "headerUserToken";

    // 使用路径资源匹配器进行精确匹配
    private final AntPathMatcher pathMatcher = new AntPathMatcher();
    @Autowired
    private ExcludeUrlProperties excludeUrlProperties;
    @Autowired
    private JWTUtils jwtUtils;

    /**
     * 实现全局过滤器的接口
     *
     * @param exchange 交换机
     * @param chain    是否放行
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        // TODO 1.首先获取请求的url
        String url = exchange.getRequest().getURI().getPath();
        log.info("请求的url是:  {}", url);

        // TODO 2.获取需要放行的url的列表
        List<String> excludeUrlPropertiesUrls = excludeUrlProperties.getUrls();

        // TODO 3.校验这个list集合，当集合不是空，进行判断列表的url跟集合的是不是相同的
        if (excludeUrlPropertiesUrls != null && !excludeUrlPropertiesUrls.isEmpty()) {
            for (String excludeUrl : excludeUrlPropertiesUrls) {
                // 要是精确匹配的话，需要使用路径规则匹配器
                if (pathMatcher.match(excludeUrl, url)) {
                    log.info("该url需要放行:  {}", url);
                    return chain.filter(exchange);
                }
            }
        }

        // TODO 获取ststic的路径
        String fileStart = excludeUrlProperties.getFileStart();
        boolean b = pathMatcher.matchStart(fileStart, url);
        if (b && StringUtils.isNotBlank(fileStart)) {
            log.info("IPLimitFilter - 放行：url = {}", url);
            return chain.filter(exchange);
        }

        String fileUpload = excludeUrlProperties.getFileUpload();
        boolean b1 = pathMatcher.matchStart(fileUpload, url);
        if (b1 && StringUtils.isNotBlank(fileStart)) {
            log.info("IPLimitFilter - 放行：url = {}", url);
            return chain.filter(exchange);
        }

        // TODO 在这会被拦截
        log.info("被拦截了......");

        // TODO 在这校验jwt是否登录
        // TODO 4.需要先获得jwt在请求头里面
        String userToken = exchange.getRequest().getHeaders().getFirst(HEADER_USER_TOKEN);

        //  TODO 5.校验userToken是否为空，对获得的token进行分割
        if (StringUtils.isNotBlank(userToken)) {
            String[] splitToken = userToken.split("@");
            // TODO 校验数组的长度，并获取分割的数据
            if (splitToken.length < 2) {
                return renderErrorMsg(exchange, ResponseStatusEnum.UN_LOGIN);
            }

            String prefix = splitToken[0];
            log.info("获得的前缀prefix: {}", prefix);

            String jwt = splitToken[1];
            log.info("获得的jwt: {}", jwt);

            // TODO 校验jwt是否正确 如果jwt正确，放行
            if (prefix.equalsIgnoreCase(TOKEN_USER_PREFIX)) {
                return dealJWT(jwt, exchange, chain, APP_USER_JSON);
            } else if (prefix.equalsIgnoreCase(TOKEN_SAAS_PREFIX)) {
                return dealJWT(jwt, exchange, chain, SAAS_USER_JSON);
            } else if (prefix.equalsIgnoreCase(TOKEN_ADMIN_PREFIX)) {
                return dealJWT(jwt, exchange, chain, ADMIN_USER_JSON);
            }

        }

        // TODO 这里的都是漏网之鱼
        return renderErrorMsg(exchange, ResponseStatusEnum.UN_LOGIN);

    }

    /**
     * 1. 自定义返回的包装类，返回一个json的格式
     * 网关无法进行全局异常处理，自定义异常无法捕获网关层的异常
     *
     * @param exchange   交换机
     * @param statusEnum 异常的枚举类
     * @return
     */
    private Mono<Void> renderErrorMsg(ServerWebExchange exchange,
                                      ResponseStatusEnum statusEnum) {

        // TODO 1. 获得返回的response
        ServerHttpResponse response = exchange.getResponse();

        // TODO 2. 构建返回的json
        GraceJSONResult JSONResult = GraceJSONResult.exception(statusEnum);

        // TODO 3. 构建返回的code值为500
        response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);

        // TODO 4. 构建返回的Content-Type,判断是否存在，不存在就新增一个
        if (!response.getHeaders().containsKey("Content-Type")) {
            response.getHeaders().set("Content-Type", "application/json;charset=UTF-8");
        }

        // TODO 5. 转换成Mono<Void> 类型返回
        DataBuffer dataBuffer = response.bufferFactory().wrap(new Gson().toJson(JSONResult).getBytes(StandardCharsets.UTF_8));
        return response.writeWith(Mono.just(dataBuffer));

    }

    /**
     * 校验jwt，因为这里有可能会抛出异常
     *
     * @param jwt
     * @param exchange
     * @param chain
     * @return
     */
    private Mono<Void> dealJWT(String jwt, ServerWebExchange exchange, GatewayFilterChain chain, String headerKey) {

        try {
            String checkJWT = jwtUtils.checkJWT(jwt);
            log.info("Checking JWT {}", checkJWT);
            // TODO 这里可以通过重构request的方式来将用户信息向后传递
            ServerWebExchange newExchange = getExchange(exchange, headerKey, checkJWT);
            return chain.filter(newExchange);
        } catch (Exception e) {
            log.error("Error checking JWT", e);
            return renderErrorMsg(exchange, ResponseStatusEnum.JWT_SIGNATURE_ERROR);
        }

    }

    /**
     * 重新构建request
     *
     * @param exchange
     * @param headerKey
     * @param headerValue
     * @return
     */
    public ServerWebExchange getExchange(ServerWebExchange exchange, String headerKey, String headerValue) {
        // TODO 1.通过exchange获得request
        ServerHttpRequest request = exchange.getRequest().mutate().header(headerKey, headerValue).build();
        // TODO 2. 构建一个新的exchange
        ServerWebExchange newExchange = exchange.mutate().request(request).build();
        return newExchange;
    }

    @Override
    public int getOrder() {
        log.error("SecurityFilterJWT 执行顺序为 0，开始执行");
        return 0;
    }
}












